package com.maezia.tdr.common;

import com.maezia.tdr.common.annotation.DataEntity;
import com.maezia.tdr.common.enums.FieldSourceEnum;
import com.maezia.tdr.domain.po.ReportFieldRel;
import com.maezia.tdr.service.ReportFieldRelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Component
public class DataEntityProvider implements ApplicationRunner {
    private static String DEFAULT_SET_METHOD_NAME = "set";

    private static Map<Class, Map<String, Method>> POJO_SET_METHOD_MAP = new HashMap<>();

    @Autowired
    private ReportFieldRelService reportFieldRelService;

    public <T> T copyProperties(Map<String, Object> source, Class target, String clientId,FieldSourceEnum sourceEnum){
        try {
            if(CollectionUtils.isEmpty(source) || StringUtils.isEmpty(clientId) || ObjectUtils.isEmpty(target) || ObjectUtils.isEmpty(sourceEnum)){
                return null;
            }

            Object instance = null;
            Map<String, Method> methodMap = POJO_SET_METHOD_MAP.get(target);
            if (!CollectionUtils.isEmpty(methodMap)) {
                instance = target.newInstance();
                List<ReportFieldRel> byType = reportFieldRelService.getByType(clientId, sourceEnum.getCode());
                Map<String, String> mapping = CollectionUtils.isEmpty(byType) ? null : byType.stream().filter(field ->
                        !StringUtils.isEmpty(field.getFieldName()) && !StringUtils.isEmpty(field.getOtherFieldName())).collect(
                            Collectors.toMap(other -> other.getOtherFieldName().trim(), name -> name.getFieldName().trim()));
                if(!CollectionUtils.isEmpty(mapping)){
                    for(Map.Entry<String, String> entry : mapping.entrySet()) {
                        Object value = source.get(entry.getKey());
                        Method method = methodMap.get(entry.getValue());
                        if(!ObjectUtils.isEmpty(method) && !ObjectUtils.isEmpty(value)){
                            String typeName = method.getParameters()[0].getType().getName();
                            switch (typeName){
                                case "java.math.BigDecimal":
                                    method.invoke(instance, new BigDecimal(value.toString()));
                                    break;
                                case "java.lang.Integer":
                                    method.invoke(instance, Integer.valueOf(value.toString()));
                                    break;
                                case "java.lang.Double":
                                    method.invoke(instance, Double.valueOf(value.toString()));
                                    break;
                                case "java.lang.Boolean":
                                    break;
                                default:
                                    method.invoke(instance, value);
                                    break;
                            }
                        }
                    }
                }
            }
            return (T) instance;
        }catch (Exception e){
            log.error("Exception:", e);
            return null;
        }
    }

    private String getMethodName(String fieldName,String methodName) {
        return methodName + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new AnnotationTypeFilter(DataEntity.class));
        Set<BeanDefinition> dataEntitys = provider.findCandidateComponents("com.maezia.tdr");
        if(!CollectionUtils.isEmpty(dataEntitys)){
            for (BeanDefinition dataEntity : dataEntitys) {
                try {
                    Map<String, Method> setMethodMap = new HashMap<>();

                    Class<?> clazz = Class.forName(dataEntity.getBeanClassName());
                    Field[] declaredFields = clazz.getDeclaredFields();
                    for(Field field : declaredFields) {
                        String fieldName = field.getName();
                        setMethodMap.put(fieldName, clazz.getMethod(getMethodName(fieldName, DEFAULT_SET_METHOD_NAME), field.getType()));
                    }
                    POJO_SET_METHOD_MAP.put(clazz, setMethodMap);
                } catch (Exception e) {
                    log.error("Exception:", e);
                }
            }
        }
    }
}
